简介
RecyclerView是我们开发过程中经常使用到的一个元素,原生的RecyclerView.Adapter基本上可以满足一般的需求,关于RecyclerView的基础介绍请移步:
RecyclerView基础使用
关于多类型的Item,原生的Adapter可以通过getItemViewType返回对应的ViewHolder类型,然后在onCreateViewHolder传入的type参数,生成不同的ViewHolder,更要命的是数据绑定过程中onBindViewHolder只传入ViewHolder对象和position,针对不同的ViewHolder,我们只能通过instanceof和强制类型装换来对不同的ViewHolder进行不同的数据操作。
这样做的弊端就是,后续如果需要添加一个类型,几乎需要修改Adapter类中的每一个方法,不利于维护。
类结构
目的
- 便于维护。增删Item的类型不需要修改Adapter的代码;
- 条例清晰。不同类型的Item对应不同的Bean类,对应不用的ViewHolder,对应不同的layout, item type <-> layout <—> ViewHolder <-> java bean
- 工厂接口。抽象出TypeFactory接口,针对同一个Bean,可以使用不同的layout和不同的ViewHolder。
思路
- 所有Bean类实现统一Visitable接口,实现type方法,返回对应的layout id;
- 创建BaseViewHolder使用泛型,T为Bean类。使用SparseArray存储View,View的id作为唯一标识。
- 具体的ViewHolder类,继承BaseViewHolder,传入不同的Bean类,方便不同的ViewHolder中绑定不同的数据。
- Adapter实现中使用Visitable List同一处理列表数据,使用事项TypeFactory接口的实例返回ItemType,viewHolder等
代码结构
代码内容
Visitable接口
1 2 3
| public interface Visitable { int type(TypeFactory typeFactory); }
|
TypeFactory接口
1 2 3 4 5 6 7
| public interface TypeFactory { int type(BannerBean bannerBean); int type(ContentBean contentBean); int type(SearchBean searchBean); int type(DividerBean dividerBean); BaseViewHolder createViewHolder(int type, View itemView); }
|
BaseViewHolder抽象类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public abstract class BaseViewHolder<T> extends RecyclerView.ViewHolder{ SparseArray<View> mViews; View mItemView;
public BaseViewHolder(View itemView) { super(itemView); mItemView = itemView; mViews = new SparseArray<>(); }
public View getView(int resId) { View view = mViews.get(resId);
if(view== null) { view = mItemView.findViewById(resId); mViews.put(resId, view); } return view; }
public abstract void bindViewData(T data); }
|
TypeFactory实现类: ItemTypeFactory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class ItemTypeFactory implements TypeFactory { public static final int BANNER_ITEM_LAYOUT = R.layout.rv_item_banner; public static final int CONTENT_ITEM_LAYOUT = R.layout.rv_item_content; public static final int SEARCH_ITEM_LAYOUT = R.layout.rv_item_search; public static final int DIVIDER_ITEM_LAYOUT = R.layout.rv_item_divider;
@Override public int type(BannerBean bannerBean) { return BANNER_ITEM_LAYOUT; }
@Override public int type(ContentBean contentBean) { return CONTENT_ITEM_LAYOUT; }
@Override public int type(DividerBean dividerBean){ return DIVIDER_ITEM_LAYOUT; }
@Override public int type(SearchBean dividerBean){ return SEARCH_ITEM_LAYOUT; }
@Override public BaseViewHolder createViewHolder(int type, View itemView) { switch (type) { case BANNER_ITEM_LAYOUT: return new BannerViewHolder(itemView); case SEARCH_ITEM_LAYOUT: return new SearchViewHolder(itemView); case CONTENT_ITEM_LAYOUT: return new ContentViewHolder(itemView); case DIVIDER_ITEM_LAYOUT: return new DividerViewHolder(itemView); default: return null; } } }
|
各种Java Bean类
BannerBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class BannerBean implements Visitable { int[] mResIds;
public BannerBean(int[] mResIds) { this.mResIds = mResIds; }
public int[] getmResIds() { return mResIds; }
public void setmResIds(int[] mResIds) { this.mResIds = mResIds; }
@Override public int type(TypeFactory typeFactory) { return typeFactory.type(this); } }
|
1 2 3 4 5 6 7 8 9 10 11 12
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="200dp">
<cn.bingoogolapple.bgabanner.BGABanner android:id="@+id/bgabanner_header" android:layout_width="match_parent" android:layout_height="200dp"> </cn.bingoogolapple.bgabanner.BGABanner>
</LinearLayout>
|
ContentBean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class ContentBean implements Visitable{ String content;
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
public ContentBean(String content) {
this.content = content; }
@Override public int type(TypeFactory typeFactory) { return typeFactory.type(this); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@+id/tv_content" android:textStyle="italic" android:fontFamily="sans-serif-condensed" android:textSize="16sp" android:gravity="center_horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
|
DividerBean
1 2 3 4 5 6 7
| public class DividerBean implements Visitable{
@Override public int type(TypeFactory typeFactory) { return typeFactory.type(this); } }
|
1 2 3 4 5 6 7
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/divider">
</LinearLayout>
|
SearchBean
1 2 3 4 5 6
| public class SearchBean implements Visitable { @Override public int type(TypeFactory typeFactory) { return typeFactory.type(this); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="50dp" android:padding="6dp">
<EditText android:layout_marginLeft="30dp" android:id="@+id/et_search" android:layout_width="0dp" android:layout_weight="9" android:maxLines="1" android:textSize="10sp" android:paddingLeft="10dp" android:paddingRight="10dp" android:hint="输入文字搜索..." android:background="@drawable/bg_search" android:layout_height="match_parent" />
<ImageView android:id="@+id/bt_search" android:layout_width="0dp" android:layout_weight="2" android:src="@drawable/search" android:layout_height="match_parent" /> </LinearLayout>
|
MultiRecyclerAdapter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| public class MultiRecyclerAdapter extends RecyclerView.Adapter<BaseViewHolder> { List<Visitable> mData; TypeFactory typeFactory;
public MultiRecyclerAdapter(List<Visitable> mData) { this.mData = mData; this.typeFactory = new ItemTypeFactory(); }
@Override public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false); return typeFactory.createViewHolder(viewType, view); }
@Override public void onBindViewHolder(BaseViewHolder holder, int position) { holder.bindViewData(mData.get(position)); }
@Override public int getItemViewType(int position) { return mData.get(position).type(typeFactory); }
@Override public int getItemCount() { return (mData != null ? mData.size() : 0); } }
|
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| public class MultiActivity extends AppCompatActivity {
@BindView(R.id.rv_content) RecyclerView rvContent;
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_weather); ButterKnife.bind(this);
List<Visitable> beans = new ArrayList<>(); int[] resIds = {R.mipmap.banner_one, R.mipmap.banner_two, R.mipmap.banner_three, R.mipmap.banner_four, R.mipmap.banner_five}; beans.add(new BannerBean(resIds)); beans.add(new SearchBean()); beans.add(new DividerBean()); beans.add(new ContentBean("one")); beans.add(new ContentBean("one")); beans.add(new ContentBean("one")); beans.add(new DividerBean()); beans.add(new ContentBean("two")); beans.add(new ContentBean("two")); beans.add(new ContentBean("two")); beans.add(new DividerBean()); beans.add(new ContentBean("three")); beans.add(new ContentBean("three")); beans.add(new ContentBean("three")); beans.add(new DividerBean()); beans.add(new ContentBean("four")); beans.add(new ContentBean("four")); beans.add(new ContentBean("four")); beans.add(new DividerBean());
MultiRecyclerAdapter multiRecyclerAdapter = new MultiRecyclerAdapter(beans); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
rvContent.setAdapter(multiRecyclerAdapter); rvContent.setLayoutManager(linearLayoutManager);
} }
|
效果
写在最后
思路来源:
https://medium.com/@dpreussler/writing-better-adapters-1b09758407d2
github上也有很多开源的万能Adapter,但是个人感觉这个思路虽然实现上代码会相对较多,但是层次分析,便于维护,扩展性较好。想要添加一个新类型的ItemType,只需要稍作修改即可快速完成,不用修改Adapter的代码。